package com.ld.shieldsb.common.core.util.properties;

import java.io.File;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import org.apache.commons.configuration2.FileBasedConfiguration;
import org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.FileBasedBuilderParameters;
import org.apache.commons.configuration2.builder.fluent.Parameters;
import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
import org.apache.commons.configuration2.convert.ListDelimiterHandler;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.io.FileHandler;
import org.apache.commons.configuration2.reloading.PeriodicReloadingTrigger;

import com.ld.shieldsb.common.core.collections.ListUtils;
import com.ld.shieldsb.common.core.model.Result;
import com.ld.shieldsb.common.core.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 参数管理类
 * 
 * @ClassName SysconfigAction
 * @author <a href="mailto:donggongai@126.com" target="_blank">kevin</a>
 * @date 2014-6-21 下午2:37:40
 * 
 */
@Slf4j
public abstract class ConfigAbstractUtil<T extends FileBasedConfiguration> implements ConfigFile {
    protected String fileName;
    protected ReloadingFileBasedConfigurationBuilder<T> reloadbuilder = null;
    protected char listDelimiter; // list分隔符
    protected ListDelimiterHandler listDelimiterHandler = null; // 禁用分隔符 new DisabledListDelimiterHandler()
    public static final char LIST_DELIMITER = '^';

    /**
     * 
     * @Title 构造函数（不公开） <br>
     * @author 吕凯 <br>
     * @date 2017年12月20日 下午5:33:29 <br>
     * @param fileName
     *            <br>
     * @param delimiterParsingDisabled
     *            分隔符是否禁用<br>
     * @param listDelimiter
     *            分隔符<br>
     */
    private ConfigAbstractUtil(String fileName, boolean delimiterParsingDisabled, char listDelimiter) {
        this(fileName, null, null, delimiterParsingDisabled, listDelimiter);
    }

    public ConfigAbstractUtil(URL url) {
        this(url, false, LIST_DELIMITER);
    }

    public ConfigAbstractUtil(URL url, boolean delimiterParsingDisabled) {
        this(url, delimiterParsingDisabled, LIST_DELIMITER);
    }

    /**
     * 
     * @Title 构造函数 <br>
     * @author 吕凯 <br>
     * @date 2019年10月28日 下午4:48:51 <br>
     * @param url
     *            <br>
     * @param delimiterParsingDisabled
     *            分隔符是否禁用<br>
     * @param listDelimiter
     *            分隔符 <br>
     */
    public ConfigAbstractUtil(URL url, boolean delimiterParsingDisabled, char listDelimiter) {
        this(null, url, null, delimiterParsingDisabled, listDelimiter);
    }

    public ConfigAbstractUtil(File file) {
        this(file, false, LIST_DELIMITER);
    }

    public ConfigAbstractUtil(File file, boolean delimiterParsingDisabled) {
        this(file, delimiterParsingDisabled, LIST_DELIMITER);
    }

    public ConfigAbstractUtil(File file, boolean delimiterParsingDisabled, char listDelimiter) {
        this(null, null, file, delimiterParsingDisabled, listDelimiter);
    }

    private ConfigAbstractUtil(String fileName, URL url, File file, boolean delimiterParsingDisabled, char listDelimiter) {
        this.listDelimiter = listDelimiter;
        try {
//            1.X的写法
//            config = new T(fileName);
//            config.setReloadingStrategy(new FileChangedReloadingStrategy()); // 文件发生改变时重新加载
//            config.setListDelimiter(listDelimiter); // 默认分隔符，即内容中出现^则认为是多个值，取值时需要注意getString只取第一个
//            if (delimiterParsingDisabled) {
//                config.setDelimiterParsingDisabled(true); // 禁用分隔符
//            }
//            config.refresh();

//            2.X的写法
            Parameters params = new Parameters();
            if (!delimiterParsingDisabled) {
                listDelimiterHandler = new DefaultListDelimiterHandler(listDelimiter);
            }
            FileBasedBuilderParameters parameters = params.fileBased().setListDelimiterHandler(listDelimiterHandler); // 默认分隔符，即内容中出现^则认为是多个值，取值时需要注意getString只取第一个
            if (url != null) {
                parameters.setURL(url);
            } else if (file != null) {
                parameters.setFile(file);
            } else {
                parameters.setFileName(fileName);
            }
            // 扫描顺序
            /**
             * 从2.x版本开始，对于文件扫描策略，用接口FileLocationStrategy来实现，该接口只有一个单一的方法locate()<br>
             * 实现类有：<br>
             * ClasspathLocationStrategy：从classpath下去加载文件**（常用）**<br>
             * AbsoluteNameLocationStrategy：绝对路径。所以直接使用new File(locator.getFileName())去加载<br>
             * HomeDirectoryLocationStrategy：从user.home里去查找<br>
             * BasePathLocationStrategy：使用basePath+fileName的方式new File()<br>
             * FileSystemLocationStrategy：使用文件系统定位。比如windows是带盘符的<br>
             * ProvidedURLLocationStrategy：直接是URL的方式。所以它也可以存在于网络上~<br>
             * CombinedLocationStrategy：真正使用的。它是一个聚合，这些实现类可以构成一个扫描链来进行按照其顺序进行组合扫描，之前讲过很多类似的设计模式了 <br>
             * <br>
             * 默认顺序：<br>
             * new ProvidedURLLocationStrategy(),<br>
             * new FileSystemLocationStrategy(),<br>
             * new AbsoluteNameLocationStrategy(),<br>
             * new BasePathLocationStrategy(),<br>
             * new HomeDirectoryLocationStrategy(true),<br>
             * new HomeDirectoryLocationStrategy(false),<br>
             * new ClasspathLocationStrategy()<br>
             * 参考https://cloud.tencent.com/developer/article/1497667<br>
             */
//            parameters.setLocationStrategy(strategy);
            reloadbuilder = new ReloadingFileBasedConfigurationBuilder<T>(getModelClass()).configure(parameters);
            reloadbuilder.setAutoSave(true);
            // 3秒钟刷新一次
            PeriodicReloadingTrigger trigger = new PeriodicReloadingTrigger(reloadbuilder.getReloadingController(), null, 3,
                    TimeUnit.SECONDS);

            trigger.start();

            this.fileName = fileName;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取系统属性出错！", e);
        }
    }

    protected Class<T> getModelClass() {
        return getModelClass(this.getClass());
//        return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected Class<T> getModelClass(Class<? extends ConfigAbstractUtil> clazzParam) {
        Class<T> clazz = null;
        // this——表示当前类（UserDao）
        // this.getClass()——当前运行类的字节码（UserDao.class）
        // this.getClass().getGenericSuperclass()——当前运行类的父类(BaseDao<T>，以为User为例，那就是BaseDao<User>)
        Type type = clazzParam.getGenericSuperclass(); // generic 泛型
        if (type instanceof ParameterizedType) {
            // 强制转化“参数化类型”
            ParameterizedType parameterizedType = (ParameterizedType) type;
            // 参数化类型中可能有多个泛型参数
            Type[] types = parameterizedType.getActualTypeArguments();
            // 获取数据的第一个元素(User.class)
            clazz = (Class<T>) types[0]; // com.oa.shore.entity.User.class
        } else {
            Class<?> classType = (Class<?>) type;
            if (ConfigAbstractUtil.class.isAssignableFrom(classType)) { // 判断是否为其子类
                clazz = getModelClass((Class<? extends ConfigAbstractUtil>) type);

            }
        }
//        return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        return clazz;
    }

    private T getConfig() {
        T configuration = null;
        try {
            configuration = reloadbuilder.getConfiguration();
        } catch (ConfigurationException e) {
            log.error("加载配置文件出错！", e);
        }
        if (configuration == null) {
            log.warn("配置文件“" + fileName + "”为null");
        }
        return configuration;
    }

    /**
     * 
     * @Title 构造函数 <br>
     * @author 吕凯 <br>
     * @date 2017年12月20日 下午5:33:29 <br>
     * @param fileName
     *            配置文件名<br>
     */
    public ConfigAbstractUtil(String fileName) {
        this(fileName, false, LIST_DELIMITER);
    }

    /**
     * 
     * @Title 构造函数 <br>
     * @author 吕凯 <br>
     * @date 2017年12月20日 下午5:33:29 <br>
     * @param fileName
     *            配置文件名<br>
     * @param listDelimiter
     *            分隔符<br>
     */
    public ConfigAbstractUtil(String fileName, char listDelimiter) {
        this(fileName, false, listDelimiter);
    }

    /**
     * 
     * @Title 构造函数 <br>
     * @author 吕凯 <br>
     * @date 2017年12月20日 下午5:33:29 <br>
     * @param fileName
     *            <br>
     * @param delimiterParsingDisabled
     *            分隔符是否可用<br>
     */
    public ConfigAbstractUtil(String fileName, boolean delimiterParsingDisabled) {
        this(fileName, delimiterParsingDisabled, LIST_DELIMITER);
    }

    /**
     * 获取所有的属性
     * 
     * @Title getPropertyKeys
     * @author 吕凯
     * @date 2016年11月1日 下午3:55:58
     * @return Set<String>
     */
    @Override
    public List<PropertiesPojo> getPropertys() {
        T configuration = getConfig();
        if (configuration == null) {
            return null;
        }
        Iterator<String> keys = configuration.getKeys();
        List<PropertiesPojo> configList = new ArrayList<>();
        while (keys.hasNext()) {
            String key = keys.next();
            PropertiesPojo configModel = getPropValModel(configuration, key);
            configList.add(configModel);
        }
        return configList;
    }

    /**
     * 根据key获取配置参数的model
     * 
     * @Title getPropValModel
     * @author 吕凯
     * @date 2019年3月6日 下午4:04:19
     * @param configuration
     * @param key
     * @return PropertiesPojo
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private PropertiesPojo getPropValModel(T configuration, String key) {
        PropertiesPojo configModel = new PropertiesPojo();
        configModel.setKey(key);
        Object valObj = configuration.getProperty(key);
        if (valObj instanceof List) { // list转为字符串，getString只能取到第一个
            List listObj = (List) valObj;
            valObj = String.join(String.valueOf(listDelimiter), listObj); //
//                valObj = StringUtils.join(valObj, listDelimiter);
        }
        configModel.setValue(valObj + "");
        configModel.setComment(getComment(configuration, key));
        return configModel;
    }

    /**
     * 获取注释
     * 
     * @Title getComment
     * @author 吕凯
     * @date 2020年6月15日 上午11:15:54
     * @param configuration
     * @param key
     * @return String
     */
    /*String comment = configuration.getLayout().getComment(key);
    if (comment != null) {
        try {
            comment = new String(comment.getBytes("iso-8859-1"));
        } catch (UnsupportedEncodingException e) {
            log.error("编码异常", e);
        }
        comment = comment.replace("#", "");
    }
    configModel.setComment(comment);*/
    protected abstract String getComment(T configuration, String key);

    /**
     * 获取属性
     * 
     * @Title getProperty
     * @author 吕凯
     * @date 2019年2月19日 上午11:11:25
     * @param key
     * @return PropertiesPojo
     */
    @Override
    public PropertiesPojo getProperty(String key) {
        T configuration = getConfig();
        if (configuration == null) {
            return null;
        }
        PropertiesPojo configModel = getPropValModel(configuration, key);
        return configModel;
    }

    /**
     * 获取注释
     * 
     * @Title getComment
     * @author 吕凯
     * @date 2019年2月19日 上午11:27:39
     * @param key
     * @return String
     */
    @Override
    public String getComment(String key) {
        T configuration = getConfig();
        if (configuration == null) {
            return null;
        }
        String comment = getComment(configuration, key);
        /*String comment = configuration.getLayout().getComment(key);
        if (comment != null) {
            try {
                comment = new String(comment.getBytes("iso-8859-1"));
            } catch (UnsupportedEncodingException e) {
                log.error("编码异常", e);
            }
            comment = comment.replace("#", "");
        }*/

        return comment;
    }

    /**
     * 获取所有key
     * 
     * @Title getKeys
     * @author 吕凯
     * @date 2017年12月20日 下午5:52:11
     * @return List<String>
     */
    @Override
    public List<String> getKeys() {
        T configuration = getConfig();
        if (configuration == null) {
            return null;
        }
        Iterator<String> keys = configuration.getKeys();
        List<String> configList = new ArrayList<>();
        while (keys.hasNext()) {
            String key = keys.next();
            configList.add(key);
        }
        return configList;
    }

    /**
     * 
     * 根据前缀获取Properties对象,返回的Properties将前缀去除
     * 
     * @Title getProperties
     * @author 吕凯
     * @date 2018年4月26日 下午6:15:53
     * @param prefix
     * @return Properties
     */
    @Override
    public Properties getProperties(String prefix) {
        if (prefix.contains(".")) { // 以.分隔，不需要写 .
            prefix = StringUtils.substringBeforeLast(prefix, ".");
        }
        return getProperties(prefix, true);
    }

    /**
     * 
     * 根据前缀获取Properties对象
     * 
     * @Title getProperties
     * @author 吕凯
     * @date 2018年4月26日 下午6:16:07
     * @param prefix
     * @param removePrefix,是否移除前缀
     * @return Properties
     */
    @Override
    public Properties getProperties(String prefix, boolean removePrefix) {
        T configuration = getConfig();
        if (configuration == null) {
            return null;
        }
        Properties properties = new Properties();
        Iterator<String> keys = configuration.getKeys(prefix);
        while (keys.hasNext()) {
            String key = keys.next();
            if (removePrefix) {
                properties.put(key.substring(prefix.length() + 1), configuration.getProperty(key));
            } else {
                properties.put(key, configuration.getProperty(key));
            }
        }
        return properties;
    }

    @Override
    public String getString(String key) {
        return getString(key, "");
    }

    @Override
    public List<Object> getList(String key) {
        T configuration = getConfig();
        if (configuration == null) {
            return null;
        }
        return configuration.getList(key);
    }

    /**
     * 获取字符型的list
     * 
     * @Title getStringList
     * @author 吕凯
     * @date 2019年1月15日 上午8:55:27
     * @param key
     * @return List<String>
     */
    @Override
    public List<String> getStringList(String key) {
        List<String> flag = new ArrayList<>();
        T configuration = getConfig();
        if (configuration == null) {
            return null;
        }
        String valStr = configuration.getString(key);
        if (StringUtils.isNotEmpty(valStr)) {
            List<Object> objList = configuration.getList(key);
            if (ListUtils.isNotEmpty(objList)) {
                objList.forEach(obj -> {
                    flag.add(obj + "");
                });
            }
        }
        return flag;
    }

    /**
     * 返回属性值
     */
    @Override
    public String getString(String key, String defaultValue) {
        T configuration = getConfig();
        if (configuration == null) {
            return defaultValue;
        }
        return configuration.getString(key, defaultValue);
    }

    @Override
    public int getInt(String key, int defaultValue) {
        T configuration = getConfig();
        if (configuration == null) {
            return defaultValue;
        }
        return configuration.getInt(key, defaultValue);
    }

    @Override
    public boolean getBoolean(String key, boolean defaultValue) {
        T configuration = getConfig();
        if (configuration == null) {
            return defaultValue;
        }
        return configuration.getBoolean(key, defaultValue);
    }

    @Override
    public long getLong(String key, long defaultValue) {
        T configuration = getConfig();
        if (configuration == null) {
            return defaultValue;
        }
        return configuration.getLong(key, defaultValue);
    }

    @Override
    public double getDouble(String key, double defaultValue) {
        T configuration = getConfig();
        if (configuration == null) {
            return defaultValue;
        }
        return configuration.getDouble(key, defaultValue);
    }

    /**
     * 写入properties信息
     */
    @Override
    public Result save(String key, String value) {
        return save(key, value, null);
    }

    /**
     * 保存配置信息
     * 
     * @Title save
     * @author 吕凯
     * @date 2019年2月19日 下午12:04:22
     * @param key
     * @param value
     * @param comment
     *            void
     */
    @Override
    public Result save(String key, String value, String comment) {
        Result result = new Result();
        T configuration = getConfig();
        if (configuration == null) {
            log.error("保存失败,配置文件为空");
            result.setMessage("保存失败,配置文件为空");
            return result;
        }
        try {
            configuration.setProperty(key, value);
            saveComment(key, comment, configuration);
            reloadbuilder.save();
//            config.refresh(); // 刷新保证顺序不变
            saveCallback(key, value);
            result.setSuccess(true);
        } catch (ConfigurationException e) {
            log.error("配置信息保存失败", e);
            result.setMessage("配置已临时修改，文件写入失败，重启后配置会失效！");
        }
        return result;
    }

    /*if (StringUtils.isNotBlank(comment)) {
    String commentVal = comment;
    try {
        commentVal = new String(comment.getBytes(), "iso-8859-1");
    } catch (UnsupportedEncodingException e) {
        log.error("", e);
    }
    configuration.getLayout().setComment(key, commentVal);
    }*/
    protected abstract void saveComment(String key, String comment, T configuration);

    /**
     * 写入properties信息
     */
    @Override
    public Result save(Map<String, Object> proMap) {
        Result result = new Result();
        T configuration = getConfig();
        if (configuration == null) {
            log.error("保存失败,配置文件为空");
            result.setMessage("保存失败,配置文件为空");
            return result;
        }
        if (proMap == null || proMap.isEmpty()) {
            result.setMessage("保存的数据为空");
            return result;
        }
        for (String key : proMap.keySet()) {
            configuration.setProperty(key, proMap.get(key));
        }
        try {
            reloadbuilder.save();
//            config.refresh(); // 刷新保证顺序不变
            saveCallback(proMap);
            result.setSuccess(true);
        } catch (ConfigurationException e) {
            log.error("配置信息文件写入失败", e);
            result.setMessage("配置已临时修改，文件写入失败，重启后配置会失效！");
        }
        return result;
    }

    /**
     * 保存属性
     * 
     * @Title save
     * @author 吕凯
     * @date 2019年2月19日 下午12:10:43
     * @param list
     *            void
     */
    @Override
    public Result save(List<PropertiesPojo> list) {
        Result result = new Result();
        T configuration = getConfig();
        if (configuration == null) {
            log.error("保存失败,配置文件为空");
            result.setMessage("保存失败,配置文件为空");
            return result;
        }
        if (ListUtils.isEmpty(list)) {
            result.setMessage("保存的数据为空");
            return result;
        }
        for (PropertiesPojo model : list) {
            result = save(model.getKey(), model.getValue(), model.getComment());
            if (!result.getSuccess()) { // 遇到失败则返回
                return result;
            }
        }
        return result;
    }

    /**
     * 加载其他文件
     * 
     * @Title load
     * @author 吕凯
     * @date 2019年10月30日 上午9:33:17
     * @param fileName
     *            可以为url，file或文件路径，inputstream
     * @return Result
     */
    @Override
    public Result load(Object fileName) {
        Result result = new Result();
        try {
            FileHandler handler = reloadbuilder.getFileHandler();
            if (fileName instanceof URL) {
                handler.load((URL) fileName);
            } else if (fileName instanceof File) {
                handler.load((File) fileName);
            } else if (fileName instanceof InputStream) {
                handler.load((InputStream) fileName);
            } else if (fileName instanceof Reader) {
                handler.load((Reader) fileName);
            } else {
                handler.load(fileName + "");
            }
            result.setSuccess(true);
        } catch (ConfigurationException e) {
            log.error("加载文件失败", e);
            result.setMessage("加载文件失败！");
        }
        return result;
    }

    /**
     * 获取配置文件的Protocol
     * 
     * @Title getProtocol
     * @author 吕凯
     * @date 2019年10月30日 上午10:07:22
     * @return String
     */
    @Override
    public String getProtocol() {
        String protocol = reloadbuilder.getFileHandler().getURL().getProtocol();
        return protocol;
    }

    @Override
    public URL getURL() {
        URL path = reloadbuilder.getFileHandler().getURL();
        return path;
    }

    @Override
    public String getPath() {
        String path = reloadbuilder.getFileHandler().getPath();
        return path;
    }

    /**
     * 
     * 保存成功的回调函数
     * 
     * @Title saveCallback
     * @author 吕凯
     * @date 2017年12月20日 下午6:01:40 void
     */
    @Override
    public void saveCallback(String key, String value) {

    }

    /**
     * 
     * 保存成功的回调函数
     * 
     * @Title saveCallback
     * @author 吕凯
     * @date 2017年12月20日 下午6:01:40 void
     */
    @Override
    public void saveCallback(Map<String, Object> proMap) {

    }

    /**
     * 获取文件路径
     * 
     * @Title getFilePath
     * @author 吕凯
     * @date 2018年11月15日 下午2:08:20
     * @return String
     */
    @Override
    public String getFilePath() {
        return fileName;
    }

    /**
     * 返回List分隔符
     * 
     * @Title getListDelimiter
     * @author 吕凯
     * @date 2019年3月6日 下午3:05:35
     * @return char
     */
    @Override
    public char getListDelimiter() {
        return listDelimiter;
    }

}
